#ifndef __EPoll_HPP__
#define __EPoll_HPP__

#include <sys/epoll.h>
#include <unistd.h>
#include "MyLog.hpp"

// EPoll模型代码综合编写，当前服务于基于TCP编写的公共聊天室 MultiplayerChatRoom
namespace QiHai
{
    // LT工作模式 在EPoll中无法表示，默认自带，所以这里自拟enum用于区分
    enum WorkMode
    {
        LT,
        ET
    };

    class EPoll
    {
    private:
        int _ePollFd;  // EPoll模型文件描述符
        bool _workModeET;  // EPoll工作模式，默认LT水平触发模式
    public:
        // 构造函数
        EPoll(int size = 256);
        // 禁止EPoll对象拷贝
        EPoll(const EPoll&) = delete;
        ~EPoll();

        // 设置工作模式
        bool SetWorkMode(int mode);
        // 加入EPoll进行监听读事件
        bool AddEPollIn(int sock);
        // 加入EPoll进行监听写事件
        bool AddEPollOut(int sock);
        // 修改为sock在EPoll模型中监听读事件
        bool ModEPollIn(int sock);
        // 修改为sock在EPoll模型中监听写事件
        bool ModEPollOut(int sock);
        // 修改sock在EPoll模型中双监听事件
        bool ModeEPollInOut(int sock);
        // 删除sock在EPoll模型下的监听状态
        bool DelEPoll(int sock);
        // 等待事件就绪接口 提供存放事件的数组 期望个数(返回的数组最大个数) 设置阻塞时长（等待阻塞，其中 < 0 表示一直阻塞除非事件就绪，=0表示非阻塞 > 0会阻塞对应时长）单位毫秒-默认10s
        // 返回值：-1表示等待错误 >=0 表示事件就绪的个数，其中数组的个数就是此个数
        int WaitEPoll(epoll_event event[], int maxNum, int timeout = 10000);
    };
    
    EPoll::EPoll(int size)  // 缺省参数以声明为主
    {
        _workModeET = false;  // 表示水平边缘触发模式
        _ePollFd = epoll_create(size);  // 创建EPoll模型
        if (_ePollFd < 0)
        {
            LOG(FATAL) << "EPoll_create error!" << std::endl;
            exit(1);
        }
    }
    EPoll::~EPoll()
    {
        close(_ePollFd);
    }

    bool EPoll::SetWorkMode(int mode)
    {
        if (mode == WorkMode::ET || mode == WorkMode::LT)
        {
            _workModeET = (mode == WorkMode::LT) ? false : true;
            return true;
        }
        return false;
    }

    bool EPoll::AddEPollIn(int sock)
    {
        // 增加读事件EPoll模型中
        struct epoll_event event;
        if (_workModeET)
            event.events = (EPOLLIN | EPOLLET);
        else event.events = EPOLLIN;
        event.data.fd = sock;
        return (-1 != epoll_ctl(_ePollFd, EPOLL_CTL_ADD, sock, &event));
    }

    bool EPoll::AddEPollOut(int sock)
    {
        // 增加写事件EPoll模型中
        struct epoll_event event;
        if (_workModeET)
            event.events = (EPOLLOUT | EPOLLET);
        else event.events = EPOLLOUT;
        event.data.fd = sock;
        return (-1 != epoll_ctl(_ePollFd, EPOLL_CTL_ADD, sock, &event));
    }

    bool EPoll::ModEPollIn(int sock)
    {
        struct epoll_event event;
        if (_workModeET)
            event.events = (EPOLLIN | EPOLLET);
        else event.events = EPOLLIN;
        event.data.fd = sock;
        return (-1 != epoll_ctl(_ePollFd, EPOLL_CTL_MOD, sock, &event));
    }

    bool EPoll::ModEPollOut(int sock)
    {
        struct epoll_event event;
        if (_workModeET)
            event.events = (EPOLLOUT | EPOLLET);
        else event.events = EPOLLOUT;
        event.data.fd = sock;
        return (-1 != epoll_ctl(_ePollFd, EPOLL_CTL_MOD, sock, &event));
    }

    bool EPoll::ModeEPollInOut(int sock)
    {
        struct epoll_event event;
        if (_workModeET)
            event.events = EPOLLET;
        else event.events = 0;
        event.events |= (EPOLLIN | EPOLLOUT);
        event.data.fd = sock;
        return (-1 != epoll_ctl(_ePollFd, EPOLL_CTL_MOD, sock, &event));
    }

    bool EPoll::DelEPoll(int sock)
    {
        // 从EPoll模型中删除该文件描述符的监听状态
        return (-1 != epoll_ctl(_ePollFd, EPOLL_CTL_DEL, sock, nullptr));
    }

    int EPoll::WaitEPoll(epoll_event event[], int maxNum, int timeout)
    {
        return epoll_wait(_ePollFd, event, maxNum, timeout);
    }

}

#endif